﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SkypeKit;
using System.Security.Cryptography.X509Certificates;
using System.Windows.Forms;
using System.IO;
using System.Drawing;
using System.Threading;

namespace ISIM
{
    public class SkypeClass
    {
        SktSkype skype;
        SktAccount account;

        public SktConversation skypeLiveSession = null;

        public AudioDevice mic;
        public List<AudioDevice> micDeviceList = new List<AudioDevice>();

        public AudioDevice speaker;
        public List<AudioDevice> speakerDeviceList = new List<AudioDevice>();

        public VideoDevice myWebCam;
        public List<VideoDevice> webCamDeviceList = new List<VideoDevice>();

        private readonly object locker = new object();

        public SkypeClass()
        {
            X509Certificate2 cert = new X509Certificate2(Login.Skype.keypairPath, Login.Skype.keypairPass);

            skype = new SktSkype(cert);

            skype.NewVideo = (oid, skp) => { return new Video(oid, skp); };
            skype.NewParticipant = (oid, skp) => { return new Participant(oid, skp); };

            skype.events.OnConnect += OnConnect;
            skype.events.OnAccountStatus += OnAccountStatus;
            skype.events.OnSkypeMessage += OnSkypeMessage;

            skype.events.OnContactAvailability += new SktEvents.OnContactAvailabilityHandler(OnContactAvailability);
            skype.events.OnContactGroupChange += OnContactGroupChange;

            skype.events.OnParticipantTextStatus += new SktEvents.OnParticipantTextStatusHandler(OnParticipantTextStatus);
            skype.events.OnMessageBodyXml += new SktEvents.OnMessageBodyXmlHandler(OnMessageBodyXml);

            skype.events.OnTransferBytestransferred += OnTransferBytestransferred;
            skype.events.OnTransferStatus += OnTransferStatus;

            skype.events.OnConversationLocalLivestatus += OnConversationLocalLivestatus;
            skype.events.OnConversationSpawnConference += OnConversationSpawnConference;

            skype.events.OnSkypeAvailableDeviceListChange += OnSkypeAvailableDeviceListChange;
            skype.events.OnSkypeAvailableVideoDeviceListChange += OnSkypeAvailableVideoDeviceListChange;
            skype.events.OnParticipantVideoStatus += OnParticipantVideoStatus;
            skype.events.OnVideoStatus += OnVideoStatus;
        }

        /// <summary>
        /// Vlastní třída Participant, která dědí SktParticipant, přidává Video, kvůli přístupu k metodám a přidává další metody
        /// </summary>
        public class Participant : SktParticipant
        {
            public Video video;
            public SktVideoRenderer renderer;

            public Participant(uint objectId, SktSkype skype) : base(objectId, skype)
            {
                renderer = new SktVideoRenderer(skypeRef);
            }

            public void StartVideo()
            {
                if (renderer.IsRunning) return;

                Console.WriteLine("starting video for" + P_IDENTITY);

                video = GetVideo() as Video;

                if (video == null) return;

                if (P_IDENTITY == Global.OpenForms.mainForm.skype.GetAccountInstance().P_SKYPENAME)
                {
                    renderer.Size = new Size(100, 100);
                    renderer.Location = new Point(0, 0);

                    if (Global.OpenForms.mainForm.skype.myWebCam.name == "SHARE_SCREEN")
                        video.SelectVideoSource(SktVideo.MEDIATYPE.MEDIA_SCREENSHARING);
                    else
                        video.SelectVideoSource(SktVideo.MEDIATYPE.MEDIA_VIDEO, Global.OpenForms.mainForm.skype.myWebCam.name, Global.OpenForms.mainForm.skype.myWebCam.path);
                }
                else
                    renderer.Dock = DockStyle.Fill;

                video.part = this;

                renderer.SizeMode = PictureBoxSizeMode.Zoom;
                renderer.BorderStyle = BorderStyle.FixedSingle;
                renderer.FrameUpdateInterval = 1;
                renderer.VideoObject = video;

                video.Start();

                if (Global.OpenForms.videoRenderForm == null)
                {
                    Global.OpenForms.mainForm.Invoke(new MethodInvoker(() =>
                    {
                        Global.OpenForms.videoRenderForm = new VideoRenderForm();
                        Global.OpenForms.videoRenderForm.Controls.Add(renderer);
                        Global.OpenForms.videoRenderForm.Show();

                        renderer.Start();
                        Console.WriteLine("creating video form");
                    }));
                }
                else
                { 
                    Global.OpenForms.videoRenderForm.Invoke(new MethodInvoker(() =>
                    {
                        try
                        {
                            if (!Global.OpenForms.videoRenderForm.Controls.Contains(renderer))
                            {
                                Global.OpenForms.videoRenderForm.Controls.Add(renderer);
                                Console.WriteLine("adding renderer for" + P_IDENTITY);
                                if (P_IDENTITY == Global.OpenForms.mainForm.skype.GetAccountInstance().P_SKYPENAME)
                                    Global.OpenForms.videoRenderForm.Controls[Global.OpenForms.videoRenderForm.Controls.Count - 1].BringToFront();
                                else
                                    Global.OpenForms.videoRenderForm.Controls[Global.OpenForms.videoRenderForm.Controls.Count - 1].SendToBack();
                                Global.OpenForms.videoRenderForm.Refresh();
                            }

                            renderer.Start();
                            Console.WriteLine("video form != null starting video");
                        }
                        catch (ObjectDisposedException e)
                        {
                            MessageBox.Show("Sorry, some exception occurs.\n" + e.Message);
                        }
                    }));
                }
            }

            public void StopVideo()
            {
                if ((video != null) && (renderer != null) && (Global.OpenForms.videoRenderForm != null))
                {
                    Global.OpenForms.videoRenderForm.Invoke(new MethodInvoker(() =>
                    {
                        video.Stop();
                        renderer.Stop();
                    }));
                }
            }

            public bool IsLive()
            {
                return ((P_VOICE_STATUS == SktParticipant.VOICE_STATUS.SPEAKING) |      // in live state
                        (P_VOICE_STATUS == SktParticipant.VOICE_STATUS.VOICE_ON_HOLD) | // in live state but on hold
                        (P_VOICE_STATUS == SktParticipant.VOICE_STATUS.LISTENING));     // in live state but muted
            }
        }

        #region Speaker & Mic
        public class AudioDevice
        {
            public string name;
            public string handle;
            public AudioDevice(string name, string handle) { this.name = name; this.handle = handle; }
            public override string ToString() { return name; }
        }

        /// <summary>
        /// Načte list dostupných vstupních/výstupních zařízení
        /// </summary>
        public void UpdateAudioDevices()
        {
            micDeviceList.Clear();
            speakerDeviceList.Clear();

            List<String> deviceNames, deviceHandles, deviceProductIds;

            skype.GetAvailableRecordingDevices(out deviceHandles, out deviceNames, out deviceProductIds);
            // projede všechny dostupné zařízení
            for (int i = 0; i < deviceNames.Count; i++)
                micDeviceList.Add(new AudioDevice(deviceNames[i], deviceHandles[i]));

            skype.GetAvailableOutputDevices(out deviceHandles, out deviceNames, out deviceProductIds);
            // projede
            for (int i = 0; i < deviceNames.Count; i++)
                speakerDeviceList.Add(new AudioDevice(deviceNames[i], deviceHandles[i]));


            int micIndex = -1;
            if (Global.settingsDictionary["ISIMsettings"].ContainsKey("micDeviceSelector"))
                micIndex = int.Parse(Global.settingsDictionary["ISIMsettings"]["micDeviceSelector"]);
            
            if (((micIndex == -1) && (micDeviceList.Count > 0)) || ((micIndex > -1) && (micDeviceList.Count < micIndex)))
                micIndex = 0;
            // vybere zařízení
            if (micIndex != -1)
                mic = micDeviceList[micIndex];


            int speakerIndex = -1;
            if (Global.settingsDictionary["ISIMsettings"].ContainsKey("speakerDeviceSelector"))
                speakerIndex = int.Parse(Global.settingsDictionary["ISIMsettings"]["speakerDeviceSelector"]);

            if (((speakerIndex == -1) && (speakerDeviceList.Count > 0)) || ((speakerIndex > -1) && (speakerDeviceList.Count < speakerIndex)))
                speakerIndex = 0;
            // vybere zařízení
            if (speakerIndex != -1)
                speaker = speakerDeviceList[speakerIndex];

            // nastaví příslušná zařízení
            SetDevices(mic, speaker);

            if (Global.OpenForms.preferencesForm != null)
            {
                Global.OpenForms.preferencesForm.Invoke(new MethodInvoker(() =>
                {
                    Global.OpenForms.preferencesForm.micDeviceSelector.Items.Clear();
                    Global.OpenForms.preferencesForm.micDeviceSelector.Items.AddRange(micDeviceList.ToArray());
                    Global.OpenForms.preferencesForm.micDeviceSelector.SelectedIndex = micIndex;
                    Global.OpenForms.preferencesForm.micDeviceSelector.Refresh();

                    Global.OpenForms.preferencesForm.speakerDeviceSelector.Items.Clear();
                    Global.OpenForms.preferencesForm.speakerDeviceSelector.Items.AddRange(speakerDeviceList.ToArray());
                    Global.OpenForms.preferencesForm.speakerDeviceSelector.SelectedIndex = speakerIndex;
                    Global.OpenForms.preferencesForm.speakerDeviceSelector.Refresh();
                }));
            }
        }

        public void SetDevices(AudioDevice mic, AudioDevice speaker)
        {
            if ((mic == null) || (speaker == null)) return;

            Safe.Lock(locker, 500, () =>
            {
                skype.SelectSoundDevices(mic.handle, speaker.handle, speaker.handle);
            });
            this.mic = mic;
            this.speaker = speaker;
        }


        /// <summary>
        /// Pokud je detekováno další zařízení příslušného druhu
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnSkypeAvailableDeviceListChange(SktSkype sender, SktEvents.OnSkypeAvailableDeviceListChangeArgs e)
        {
            Safe.Lock(locker, 500, () =>
            {
                UpdateAudioDevices();
            });
        }
#endregion

        #region WebCam
        public class Video : SktVideo
        {
            public Participant part;

            public Video(uint ObjectID, SktSkype Skype) : base(ObjectID, Skype) { }
        }

        public class VideoDevice
        {
            public string name;
            public string path;
            public VideoDevice(string name, string path) { this.name = name; this.path = path; }
            public override string ToString() { return name; }
        }

        private void OnVideoStatus(SktVideo sender, SktEvents.OnVideoStatusArgs e)
        {
            Safe.Lock(locker, 1000, () =>
            {
                Video video = sender as Video;
                if (video.part != null)
                {
                    Console.WriteLine("VIDEO STATUS" + video.part.P_IDENTITY + video.P_STATUS);
                    if (video.P_STATUS == SktVideo.STATUS.AVAILABLE)
                        video.Start();
                    else if (video.P_STATUS == SktVideo.STATUS.RUNNING)
                        video.part.StartVideo();
                    else if ((e.value == SktVideo.STATUS.STOPPING) ||
                        (e.value == SktVideo.STATUS.NOT_AVAILABLE) ||
                        (e.value == SktVideo.STATUS.SWITCHING_DEVICE))
                        video.part.StopVideo();
                }
            });
        }

        private void OnParticipantVideoStatus(SktParticipant sender, SktEvents.OnParticipantVideoStatusArgs e)
        {
            Safe.Lock(locker, 1000, () =>
            {
                Participant part = sender as Participant;
                Console.WriteLine("PART VIDEO STATUS: " + part.P_IDENTITY + part.P_VIDEO_STATUS);
                if (sender.P_VIDEO_STATUS == SktParticipant.VIDEO_STATUS.VIDEO_AVAILABLE)// || (sender.P_VIDEO_STATUS == SktParticipant.VIDEO_STATUS.VIDEO_CONNECTING) || (sender.P_VIDEO_STATUS == SktParticipant.VIDEO_STATUS.STREAMING))
                {
                    part.video = part.GetVideo() as Video;
                    
                    if (part.video == null) return;

                    if (part.P_IDENTITY == account.P_SKYPENAME)
                        part.video.SelectVideoSource(SktVideo.MEDIATYPE.MEDIA_VIDEO, myWebCam.name, myWebCam.path);
                    
                    part.video.part = part;
                }
            });
        }

        /// <summary>
        /// Načte list dostupných video zařízení
        /// </summary>
        public void UpdateVideoDevices()
        {
            Safe.Lock(locker, 2500, () =>
            {
                webCamDeviceList.Clear();

                List<String> deviceNames, deviceIds;
                uint devCount;
                skype.GetAvailableVideoDevices(out deviceNames, out deviceIds, out devCount);

                webCamDeviceList.Add(new VideoDevice("SHARE_SCREEN", ""));

                int webcamIndex = -1;
                if (devCount > 0)
                {
                    // projede všechny dostupné zařízení
                    for (int i = 0; i < devCount; i++)
                        webCamDeviceList.Add(new VideoDevice(deviceNames[i], deviceIds[i]));

                    if ((Global.settingsDictionary["ISIMsettings"].ContainsKey("videoDeviceSelector")))
                        webcamIndex = int.Parse(Global.settingsDictionary["ISIMsettings"]["videoDeviceSelector"]);

                    if (((webcamIndex == -1) && (webCamDeviceList.Count > 0)) || ((webcamIndex > -1) && (webCamDeviceList.Count < webcamIndex)))
                        webcamIndex = 0;

                    // vybere zařízení
                    if (webcamIndex != -1)
                        myWebCam = webCamDeviceList[webcamIndex];

                    SetVideoDevice(myWebCam);
                }

                if (Global.OpenForms.preferencesForm != null)
                {
                    Global.OpenForms.preferencesForm.Invoke(new MethodInvoker(() =>
                    {
                        Global.OpenForms.preferencesForm.videoDeviceSelector.Items.Clear();
                        Global.OpenForms.preferencesForm.videoDeviceSelector.Items.AddRange(webCamDeviceList.ToArray());
                        Global.OpenForms.preferencesForm.videoDeviceSelector.SelectedIndex = webcamIndex;
                        Global.OpenForms.preferencesForm.videoDeviceSelector.Refresh();
                    }));
                }
            });

            
        }

        public void SetVideoDevice(VideoDevice webcam)
        {
            if (webcam == null) return;
            myWebCam = webcam;
        }

        /// <summary>
        /// Pokud je detekováno nové zařízení příslušeného druhu
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void OnSkypeAvailableVideoDeviceListChange(SktSkype sender, SktEvents.OnSkypeAvailableVideoDeviceListChangeArgs e)
        {
            Safe.Lock(locker, 500, () =>
            {
                UpdateVideoDevices();
            });
        }
        #endregion

        /// <summary>
        /// Editace zprávy - kontakt editoval zprávu -> přidá zprávu s prefixem EDIT:
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnMessageBodyXml(SktMessage sender, SktEvents.OnMessageBodyXmlArgs e)
        {
            Safe.Lock(locker, 500, () =>
            {
                Console.WriteLine(e.sender.P_AUTHOR + e.sender.P_BODY_XML);

                if ((e.sender.P_EDITED_BY != null) && (e.sender.P_AUTHOR != account.P_SKYPENAME) && (e.sender.P_TYPE == SktMessage.TYPE.POSTED_TEXT))
                {
                    Global.OpenForms.mainForm.Invoke(new System.Windows.Forms.MethodInvoker(() =>
                    {
                        ISIMContact contact;
                        if (sender.P_CONVO_ID != null)
                            contact = new SkypeContact(skype.GetContact(sender.P_AUTHOR), sender.P_CONVO_ID);
                        else if (Global.OpenForms.mainForm.contactListView.Items.ContainsKey(e.sender.P_AUTHOR))
                            contact = (Global.OpenForms.mainForm.contactListView.Items[e.sender.P_AUTHOR] as ContactListViewItem).contact;
                        else
                            contact = new SkypeContact(skype.GetContact(e.sender.P_AUTHOR), skype.GetConversationByIdentity(e.sender.P_AUTHOR));


                        Global.ISIMFunctions.AddChatMessage(contact, "EDIT: " + TextEdit.DecodeHtmlEntities(TextEdit.StripHtmlTags(e.sender.P_BODY_XML)), e.sender.P_EDIT_TIMESTAMP, e.sender.P_AUTHOR_DISPLAYNAME);
                    }));
                }
            });
        }

        /// <summary>
        /// Kontakt začal psát
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnParticipantTextStatus(SktParticipant sender, SktEvents.OnParticipantTextStatusArgs e)
        {
            Safe.Lock(locker, 500, () =>
            {
                if (e.sender.P_IDENTITY == account.P_SKYPENAME) return;

                if ((e.sender.P_TEXT_STATUS == SktParticipant.TEXT_STATUS.WRITING) || (e.sender.P_TEXT_STATUS == SktParticipant.TEXT_STATUS.WRITING_AS_ANGRY) || (e.sender.P_TEXT_STATUS == SktParticipant.TEXT_STATUS.WRITING_AS_CAT))
                {
                    // kontakt píše zprávu
                    Global.ISIMFunctions.ContactIsComposing(e.sender.P_IDENTITY);
                }
            });
        }

        /// <summary>
        /// Při připojení se ke skypekitu -> načtení daného účtu
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnConnect(object sender, SktEvents.OnConnectArgs e)
        {
            if (e.success)
            {
                Safe.Lock(locker, 2500, () =>
                {
                    // abych mohl používat apache
                    skype.SetInt(SktSetup.LocalMachine.DISABLE_PORT80, 1);
                    account = skype.GetAccount(Login.Skype.username);
                    account.LoginWithPassword(Login.Skype.password, false, false);
                    account.SetAvailability(SktContact.AVAILABILITY.ONLINE);
                    Console.WriteLine("connected to skype");
                });
            }
            else
                Console.WriteLine("IPC handshake failed");
        }

        /// <summary>
        /// Změna dostupnosti mě, jakožto přihlášenýho
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnAccountStatus(SktAccount sender, SktEvents.OnAccountStatusArgs e)
        {
            Console.WriteLine("skype" + e.value.ToString());

            if (e.value == SktAccount.STATUS.LOGGED_IN)
            {
                Global.ISIMFunctions.CloseMessage("skype_login_failed");

                Safe.Lock(locker, 10000, () =>
                {
                    Console.WriteLine("skype: Logged in");
                    // identifikace isimu
                    if (!account.P_ABOUT.Contains("::ISIM::"))
                        account.SetStrProperty((int)SktAccount.PropKeys.P_ABOUT, account.P_ABOUT + "\nClient ::ISIM::");

                    Global.OpenForms.mainForm.contactListView.Invoke(new System.Windows.Forms.MethodInvoker(() =>
                    {
                        Global.OpenForms.mainForm.contactListView.BeginUpdate();
                    }));

                    // projedou se kámoši a přidaj se
                    foreach (SktContact contact in skype.GetHardwiredContactGroup(SktContactGroup.TYPE.ALL_BUDDIES).GetContacts())
                    {
                        Global.ISIMFunctions.AddContact(new SkypeContact(contact, null));
                        Console.WriteLine("contact: " + contact.P_DISPLAYNAME);
                    }

                    foreach (SktConversation conv in skype.GetConversationList(SktConversation.LIST_TYPE.INBOX_CONVERSATIONS))
                    {
                        Global.ISIMFunctions.AddContact(new SkypeContact(null, conv));
                    }

                    Global.OpenForms.mainForm.contactListView.Invoke(new System.Windows.Forms.MethodInvoker(() =>
                    {
                        Global.OpenForms.mainForm.contactListView.EndUpdate();
                    }));

                    // projedou se nevyřízené autorizace
                    foreach (SktContact contact in skype.GetHardwiredContactGroup(SktContactGroup.TYPE.CONTACTS_WAITING_MY_AUTHORIZATION).GetContacts())
                    {
                        AuthorizeContact(contact);
                    }

                    UpdateAudioDevices();
                    UpdateVideoDevices();
                });
            }

            if (e.value == SktAccount.STATUS.LOGGED_OUT)
            {
                Console.WriteLine("SKype: Login failed");

                Global.ISIMFunctions.ShowMessage("skype_login_failed", Global.LangManager.Translate("connectionFailed"), "Skype: " + Global.LangManager.Translate("connectionFailed") + "." + Global.LangManager.Translate("checkLoginAndInternetConnection"), MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Připojení ke skype. Spuštění runtime a connect.
        /// </summary>
        public void connect()
        {
            Safe.Lock(locker, 10000, () =>
            {
                skype.LaunchRuntime(Login.Skype.runtimePath, true);
                Thread.Sleep(500);
                try
                {
                    skype.Connect();
                }
                catch (Exception)
                {
                    Console.WriteLine("cannot launch skype runtime");
                }
                Thread.Sleep(1000);

            });
        }

        /// <summary>
        /// Odhlášení se od skype protokolu
        /// </summary>
        public void disconnect()
        {
            Safe.Lock(locker, 1500, () =>
            {
                skype.Disconnect();
            });
            Console.WriteLine("dissconnected from skypekit");
        }

        /// <summary>
        /// ZMěna skupin - čekování příchozí autorizace
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnContactGroupChange(SktContactGroup sender, SktEvents.OnContactGroupChangeArgs e)
        {
            Safe.Lock(locker, 1000, () =>
            {
                Console.WriteLine("skype contact group change: " + sender.P_TYPE + sender.P_GIVEN_DISPLAYNAME);
                // zčekne příchozí autorizace
                if (sender.P_TYPE == SktContactGroup.TYPE.CONTACTS_WAITING_MY_AUTHORIZATION)
                {
                    if (!e.contact.IsMemberOfHardwiredGroup(SktContactGroup.TYPE.ALL_BUDDIES))
                        AuthorizeContact(e.contact);
                    else
                        e.contact.SetBuddyStatus(true);
                }
            });
        }

        /// <summary>
        /// Autorizace - nastaví kámoše jakože jó nebo né
        /// </summary>
        /// <param name="contact"></param>
        private void AuthorizeContact(SktContact contact)
        {
            if (MessageBox.Show(Global.LangManager.Translate("authRequest") + ": " + contact.P_SKYPENAME + "(" + contact.P_DISPLAYNAME + ")\n" + contact.P_RECEIVED_AUTHREQUEST + "\n" + Global.LangManager.Translate("accept") + "?", Global.LangManager.Translate("authRequest") + " " + contact.P_FULLNAME, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1) == DialogResult.Yes)
            {
                try
                {
                    Safe.Lock(locker, 1000, () =>
                    {
                        contact.SetBuddyStatus(true);
                    });
                }
                catch (Exception ex)
                {
                    Console.WriteLine("auth error: " + ex.Message);
                }
            }
            else
            {
                try
                {
                    Safe.Lock(locker, 1000, () =>
                    {
                        contact.SetBuddyStatus(false);
                    });
                }
                catch (Exception ex)
                {
                    Console.WriteLine("auth error: " + ex.Message);
                }
            }
        }

        /// <summary>
        /// Zpracování zprávy - taj je zpráva všechno co se hne, tzn zpracování hovorů, přenosu souborů, upozornění etc.. jo a taky zpráv
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnSkypeMessage(SktSkype sender, SktEvents.OnSkypeMessageArgs e)
        {
            Safe.Lock(locker, 1000, () =>
            {
                Console.WriteLine("message from: " + e.message.P_AUTHOR_DISPLAYNAME + e.message.P_BODY_XML);

                ISIMContact contact = null;
                Global.OpenForms.mainForm.Invoke(new System.Windows.Forms.MethodInvoker(() =>
                {
                    if ((e.conversation.P_TYPE == SktConversation.TYPE.CONFERENCE) || (!Global.OpenForms.mainForm.contactListView.Items.ContainsKey(e.message.P_AUTHOR)))
                    {
                        if ((e.message.P_TYPE == SktMessage.TYPE.POSTED_TEXT) || (e.message.P_TYPE == SktMessage.TYPE.POSTED_FILES))
                            contact = new SkypeContact(skype.GetContact(e.message.P_AUTHOR), e.conversation);
                        else
                            contact = new SkypeContact(null, e.conversation);
                    }
                    else
                        contact = (Global.OpenForms.mainForm.contactListView.Items[e.message.P_AUTHOR] as ContactListViewItem).contact;
                }));

                if (contact.id == account.P_SKYPENAME) return;

                switch (e.message.P_TYPE)
                {
                    // přijmul jsem hovor
                    case SktMessage.TYPE.STARTED_LIVESESSION:
                        skypeLiveSession = e.conversation;
                        contact.callStatus = CallStatus.isLive;

                        Global.ISIMFunctions.AddChatMessage(contact, Global.LangManager.Translate("callAccepted"), e.message.P_TIMESTAMP, Global.ISIM);

                        if (Global.OpenForms.incommingCallDialogList.ContainsKey(e.conversation.P_IDENTITY))
                            Global.OpenForms.incommingCallDialogList[e.conversation.P_IDENTITY].Invoke(new MethodInvoker(() => Global.OpenForms.incommingCallDialogList[e.conversation.P_IDENTITY].Close()));
                        break;

                    // položil jsem hovor
                    case SktMessage.TYPE.ENDED_LIVESESSION:
                        skypeLiveSession = null;
                        contact.callStatus = CallStatus.none;

                        Global.ISIMFunctions.AddChatMessage(contact, Global.LangManager.Translate("callEnded"), e.message.P_TIMESTAMP, Global.ISIM);

                        Global.OpenForms.mainForm.contactListView.Invoke(new MethodInvoker(() =>
                        {
                            if (Global.OpenForms.mainForm.contactListView.Items.ContainsKey(e.conversation.P_IDENTITY))
                                (Global.OpenForms.mainForm.contactListView.Items[e.conversation.P_IDENTITY] as ContactListViewItem).contact.callStatus = CallStatus.none;

                            Global.OpenForms.mainForm.muteMic.Checked = false;

                            // překreslení dané položky bez blikání
                            int index = Global.OpenForms.mainForm.contactListView.Items.IndexOfKey(contact.id);
                            Global.OpenForms.mainForm.contactListView.RedrawItems(index, index, false);
                        }));
                        break;

                    case SktMessage.TYPE.POSTED_FILES:
                        if (e.message.P_AUTHOR != account.P_SKYPENAME)
                            RecieveFiles(contact, e.message, e.conversation);
                        break;

                    // normální zpráva
                    case SktMessage.TYPE.POSTED_TEXT:
                        if (e.message.P_AUTHOR != account.P_SKYPENAME)
                            Global.ISIMFunctions.AddChatMessage(contact, TextEdit.DecodeHtmlEntities(TextEdit.StripHtmlTags(e.message.P_BODY_XML)), e.message.P_TIMESTAMP, e.message.P_AUTHOR_DISPLAYNAME);
                        break;

                    // další
                    default:
                        if (e.message.P_AUTHOR != account.P_SKYPENAME)
                            Global.ISIMFunctions.AddChatMessage(contact, "Undefined operation:" + e.message.P_TYPE.ToString(), e.message.P_TIMESTAMP, e.message.P_AUTHOR_DISPLAYNAME);
                        break;
                }
            });
        }

        private void UpdateTransferStatus(SktTransfer transferInfo)
        {
            Safe.Lock(locker, 200, () =>
            {
                string transferId = transferInfo.P_PARTNER_HANDLE + transferInfo.P_FILENAME + transferInfo.P_CONVO_ID;
                if (Global.OpenForms.fileTransferringDialogList.ContainsKey(transferId))
                {
                    Global.OpenForms.fileTransferringDialogList[transferId].Invoke(new MethodInvoker(() =>
                    {
                        try
                        {
                            Global.OpenForms.fileTransferringDialogList[transferId].UpdateTransferProgress();
                        }
                        catch (ObjectDisposedException)
                        {
                            Global.OpenForms.fileTransferringDialogList.Remove(transferId);
                        }
                    }));
                }
                else
                {
                    if (transferInfo.P_STATUS != SktTransfer.STATUS.CANCELLED)
                    {
                        Global.OpenForms.mainForm.Invoke(new MethodInvoker(() =>
                        {
                            bool dontActivate = false;
                            if (Global.settingsDictionary["ISIMsettings"].ContainsKey("dontActivateChatWindow"))
                                dontActivate = bool.Parse(Global.settingsDictionary["ISIMsettings"]["dontActivateChatWindow"]);

                            FileTransferringDialog fileDialog = new FileTransferringDialog(transferId, transferInfo);
                            if ((Global.dnd) || (dontActivate)) fileDialog.WindowState = FormWindowState.Minimized;
                            fileDialog.Show();
                            if ((!Global.dnd) && (!dontActivate)) fileDialog.Activate();
                        }));
                    }
                }
            });
        }

        void OnTransferBytestransferred(SktTransfer sender, SktEvents.OnTransferBytestransferredArgs e)
        {
            UpdateTransferStatus(sender);
        }

        void OnTransferStatus(SktTransfer sender, SktEvents.OnTransferStatusArgs e)
        {
            UpdateTransferStatus(sender);
        }

        /// <summary>
        /// Kontakt poslal do chatu soubory
        /// </summary>
        /// <param name="contact"></param>
        /// <param name="sktMessage"></param>
        /// <param name="sktConversation"></param>
        private void RecieveFiles(ISIMContact contact, SktMessage message, SktConversation conversation)
        {
            Safe.Lock(locker, 1000, () =>
            {
                SktTransfer.List transferList = message.GetTransfers();
                // oznámení o příchozích souborech, pokud je kontakt blokovaný, zároveň se mu automaticky odešle zpráva, že je blokovaný a soubory se odmítnou
                Global.ISIMFunctions.AddChatMessage(contact, Global.LangManager.Translate("incommingFiles"), DateTime.Now, Global.Converter.GetRenamedContactName(contact));

                if (Global.settingsDictionary["bannedList"].ContainsKey(contact.id))
                {
                    for (int i = 0; i < transferList.Count; i++)
                        transferList[i].Cancel();

                    return;
                }

                Global.OpenForms.mainForm.Invoke(new MethodInvoker(() =>
                {
                    bool dontActivate = false;
                    if (Global.settingsDictionary["ISIMsettings"].ContainsKey("dontActivateChatWindow"))
                        dontActivate = bool.Parse(Global.settingsDictionary["ISIMsettings"]["dontActivateChatWindow"]);

                    IncommingTransferDialog fileDialog = new IncommingTransferDialog(contact, transferList);
                    if ((Global.dnd) || (dontActivate)) fileDialog.WindowState = FormWindowState.Minimized;
                    fileDialog.Show();
                    if ((!Global.dnd) && (!dontActivate)) fileDialog.Activate();
                }));
            });
        }

        public void SendFiles(SktConversation conv, List<string> paths)
        {
            Safe.Lock(locker, 1000, () =>
            {
                SktSkype.TRANSFER_SENDFILE_ERROR error_code;
                string error_file;
                SktMessage message;
                conv.PostFiles(out error_code, out error_file, out message, paths, Global.LangManager.Translate("sendingFiles"));

                Console.WriteLine("sendfiles " + error_file + "+" + error_code.ToString());
            });
        }

        /// <summary>
        /// Změna dostupnosti kontaktu
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnContactAvailability(SktContact sender, SktEvents.OnContactAvailabilityArgs e)
        {
            // moooooc komba bž xD - no už to nejsou takový komba jako dřív tak nepodělávé
            Safe.Lock(locker, 1500, () =>
            {
                if (sender.P_SKYPENAME == account.P_SKYPENAME) return;

                if (!sender.IsMemberOfHardwiredGroup(SktContactGroup.TYPE.ALL_BUDDIES))
                {
                    sender.SetBuddyStatus(false);
                    return;
                }


                if (!Global.Converter.AvatarExists(sender.P_SKYPENAME))
                {
                    byte[] contactAvatar = sender.P_AVATAR_IMAGE;
                    if (contactAvatar != null)
                    {
                        MemoryStream ms = new MemoryStream(contactAvatar);
                        Global.ISIMFunctions.SaveAvatar(Image.FromStream(ms), sender.P_SKYPENAME);
                    }
                }

                Global.OpenForms.mainForm.contactListView.BeginInvoke(new System.Windows.Forms.MethodInvoker(() =>
                {
                    if (Global.OpenForms.mainForm.contactListView.Items.ContainsKey(sender.P_SKYPENAME))
                        Global.ISIMFunctions.EditContactAvailability(sender.P_SKYPENAME, Global.Converter.GetContactAvailability(sender));
                    else
                        Global.ISIMFunctions.AddContact(new SkypeContact(sender, null));
                }));
            });

            Console.WriteLine("sender - av:" + sender.P_DISPLAYNAME + sender.P_AVAILABILITY);
        }

        /// <summary>
        /// Vrátí avatar kontaktu
        /// </summary>
        /// <param name="conv"></param>
        /// <returns></returns>
        public Image getAvatarFromConversation(SktConversation conv)
        {
            Image _avatar = null;
            Safe.Lock(locker, 1000, () =>
            {
                if (conv.GetParticipants().Count == 2)
                {
                    byte[] contactAvatar = skype.GetContact(conv.P_IDENTITY).P_AVATAR_IMAGE;
                    if (contactAvatar != null)
                    {
                        MemoryStream imageStream = new MemoryStream(contactAvatar);

                        Image avatar = Image.FromStream(imageStream);

                        Global.ISIMFunctions.SaveAvatar(avatar, conv.P_IDENTITY);

                        _avatar = avatar;
                    }
                }
            });
            if (_avatar != null)
                return _avatar;
            else
                return Global.Converter.GetContactAvatar(conv.P_IDENTITY);
        }

        /// <summary>
        /// Změna dostupnosti
        /// </summary>
        /// <param name="availability"></param>
        public void changeMyAvailability(SktContact.AVAILABILITY availability)
        {
            Safe.Lock(locker, 1000, () =>
            {
                account.SetAvailability(availability);
            });
        }

        /// <summary>
        /// Odeslání zprávy
        /// </summary>
        /// <param name="conv"></param>
        /// <param name="message"></param>
        public void SendMessage(SktConversation conv, string message)
        {
            Safe.Lock(locker, 1000, () =>
            {
                conv.PostText(message, false);
            });
        }

        /// <summary>
        /// Smaže kontakt
        /// </summary>
        /// <param name="contact"></param>
        public void RemoveContact(SktContact contact)
        {
            Safe.Lock(locker, 1000, () =>
            {
                contact.SetBuddyStatus(false);
            });
        }

        /// <summary>
        /// Blokuje nebo unblokuje kontakt
        /// </summary>
        /// <param name="contact"></param>
        /// <param name="block"></param>
        public void BlockContact(SktContact contact, bool block)
        {
            Safe.Lock(locker, 1000, () =>
            {
                contact.SetBlocked(block);
            });
        }

        /// <summary>
        /// Odešle info danému kontaktu o tom, že píšu zprávu
        /// </summary>
        /// <param name="conv"></param>
        public void ImTypingMessage(SktConversation conv)
        {
            Safe.Lock(locker, 500, () =>
            {
                conv.SetMyTextStatusTo(SktParticipant.TEXT_STATUS.WRITING_AS_CAT);
            });
        }

        /// <summary>
        /// Zčekne, jestli account obsahuje nějaká data
        /// </summary>
        /// <returns></returns>
        public bool connectedToSkypeKit()
        {
            return ((account != null) && (account.P_STATUS != SktAccount.STATUS.LOGGED_OUT));
        }

        /// <summary>
        /// Odeslání žádosti o autorizaci
        /// </summary>
        /// <param name="skypename"></param>
        /// <returns></returns>
        public bool requestContactAuth(string skypename, string authText)
        {
            bool success = false;
            Safe.Lock(locker, 1000, () =>
            {
                try
                {
                    SktContact contact = skype.GetContact(skypename);
                    contact.SetBuddyStatus(true, true);
                    contact.SendAuthRequest(authText);
                    success = true;
                }
                catch
                {
                    success = false;
                }
            });
            return success;
        }

        /// <summary>
        /// Nastaví stav uživatele - krátký text
        /// </summary>
        /// <param name="status"></param>
        public void SetMyMoodStatus(string status)
        {
            Safe.Lock(locker, 500, () =>
            {
                account.SetStrProperty((int)SktAccount.PropKeys.P_MOOD_TEXT, status);
            });
        }

        /// <summary>
        /// Zavolá članům dané konverzace v případě, že alespoň jeden z nich je již v hovoru, připojí se k němu
        /// </summary>
        /// <param name="conv"></param>
        public void MakeCall(SktConversation conv)
        {
            Safe.Lock(locker, 1000, () =>
            {
                SktParticipant.List parts;
                parts = conv.GetParticipants();
                List<String> names = new List<string>();
                bool isLive = false;
                foreach (Participant part in parts)
                {
                    names.Add(part.P_IDENTITY);
                    if (part.IsLive())
                    {
                        isLive = true;
                        break;
                    }
                }

                if (isLive)
                    conv.JoinLiveSession();
                else
                    conv.RingOthers(names);
            });
        }


        /// <summary>
        /// Ukončí hovor dané konverzace
        /// </summary>
        /// <param name="conv"></param>
        public void EndCall(SktConversation conv)
        {
            Safe.Lock(locker, 1000, () =>
            {
                conv.LeaveLiveSession();
            });
        }

        /// <summary>
        /// Příjme hovor - pokud hovor probíhá vytvoří konferenci
        /// </summary>
        /// <param name="conv"></param>
        public void AnswerCall(SktConversation conv)
        {
            Safe.Lock(locker, 1000, () =>
            {
                List<string> identities = new List<string>();
                SktParticipant.List participants = conv.GetParticipants();
                foreach (SktParticipant participant in participants)
                    identities.Add(participant.P_IDENTITY);

                // pokud s nikym nevolám jen to zvednu
                if (skypeLiveSession == null)
                    conv.JoinLiveSession();
                else
                {
                    // pokud s nekym volam vytvori konferenci a pripoji ho k hovoru
                    if (skypeLiveSession.P_LOCAL_LIVESTATUS == SktConversation.LOCAL_LIVESTATUS.IM_LIVE)
                    {
                        skypeLiveSession = skypeLiveSession.Assimilate(conv);
                        List<string> identity = new List<string>();
                        foreach (Participant participant in participants)
                        {
                            if ((!participant.IsLive()) && (participant.P_IDENTITY != account.P_SKYPENAME))
                                identity.Add(participant.P_IDENTITY);
                        }
                        skypeLiveSession.RingOthers(identity);
                        Console.WriteLine("assimilate");
                    }
                    else
                    {
                        skypeLiveSession = skypeLiveSession.SpawnConference(identities);
                        Console.WriteLine("spawn");
                    }
                }
            });
        }

        /// <summary>
        /// pokud s někým volám, připojím ho k hovoru a zavolám mu jinak se jen vytvoří konference
        /// </summary>
        /// <param name="conv"></param>
        public void AddToConference(SktConversation selectedConv, SktConversation conv)
        {
            // pokavad už s někým volám, přidá se do konference vybraný kontakt a zavolá semu
            if (skypeLiveSession != null)
                AnswerCall(conv);
            else
            {
                List<string> identities = new List<string>();
                foreach (SktParticipant participant in conv.GetParticipants())
                    identities.Add(participant.P_IDENTITY);

                // co sme vyčetl spawnconference vytvoří novou konverzaci typu conference
                // assimilate spojí dvě živé konference tedy pokud s někým volám a někdo mi volá
                if (selectedConv.P_TYPE != SktConversation.TYPE.CONFERENCE)
                    selectedConv.SpawnConference(identities);
                else
                    selectedConv.AddConsumers(identities);
            }
        }

        /// <summary>
        /// Příchozí hovor
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnConversationLocalLivestatus(SktConversation sender, SktEvents.OnConversationLocalLivestatusArgs e)
        {
            if (e.value == SktConversation.LOCAL_LIVESTATUS.RINGING_FOR_ME)
            {
                ISIMContact contact = null;
                Global.OpenForms.mainForm.Invoke(new System.Windows.Forms.MethodInvoker(() =>
                {
                    if (Global.OpenForms.mainForm.contactListView.Items.ContainsKey(sender.P_IDENTITY))
                        contact = (Global.OpenForms.mainForm.contactListView.Items[sender.P_IDENTITY] as ContactListViewItem).contact;
                    else
                        contact = new SkypeContact(skype.GetContact(sender.P_IDENTITY), sender);
                }));

                if ((Global.settingsDictionary["bannedList"].ContainsKey(e.sender.P_IDENTITY)) || (Global.dnd))
                {
                    Safe.Lock(locker, 500, () =>
                    {
                        sender.LeaveLiveSession();
                    });

                    // mu řekne, že bohužel...
                    Global.ISIMFunctions.AddChatMessage(contact, Global.LangManager.Translate("incommingCall"), DateTime.Now, sender.P_DISPLAYNAME);
                    return;
                }

                // incomming call dialog
                Global.OpenForms.mainForm.Invoke(new MethodInvoker(() =>
                {
                    IncommingCallDialog callDialog = new IncommingCallDialog(contact);
                    callDialog.Show();
                    callDialog.Activate();
                }));
            }

            if (e.value == SktConversation.LOCAL_LIVESTATUS.IM_LIVE)
            {
                ISIMContact contact = null;
                Global.OpenForms.mainForm.Invoke(new System.Windows.Forms.MethodInvoker(() =>
                {
                    if (Global.OpenForms.mainForm.contactListView.Items.ContainsKey(sender.P_IDENTITY))
                        contact = (Global.OpenForms.mainForm.contactListView.Items[sender.P_IDENTITY] as ContactListViewItem).contact;
                    else
                        contact = new SkypeContact(skype.GetContact(sender.P_IDENTITY), sender);
                    contact.callStatus = CallStatus.isLive;

                    // překreslení dané položky bez blikání
                    int index = Global.OpenForms.mainForm.contactListView.Items.IndexOfKey(contact.id);
                    Global.OpenForms.mainForm.contactListView.RedrawItems(index, index, false);
                }));
            }
        }

        private void OnConversationSpawnConference(SktConversation sender, SktEvents.OnConversationSpawnConferenceArgs e)
        {
            if (sender == skypeLiveSession) skypeLiveSession = e.spawned;
            Global.OpenForms.mainForm.contactListView.Invoke(new MethodInvoker(() =>
            {
                if (!Global.OpenForms.mainForm.contactListView.Items.ContainsKey(e.spawned.P_IDENTITY))
                {
                    ISIMContact contact = new SkypeContact(null, e.spawned);
                    Global.ISIMFunctions.AddContact(contact);
                    Global.ISIMFunctions.AddChatMessage(contact, Global.LangManager.Translate("conferenceSpawned"), e.spawned.P_CREATION_TIMESTAMP, e.spawned.P_DISPLAYNAME);
                }
            }));
        }

        /// <summary>
        /// Smaže konferenci nevím co z toho a bže tomu nevěřim, tak to volám všecko
        /// </summary>
        /// <param name="conversation"></param>
        public void LeaveConference(SktConversation conversation)
        {
            conversation.LeaveLiveSession();
            conversation.Delete();
            conversation.RetireFrom();
            conversation.RemoveFromInbox();
        }

        /// <summary>
        /// Mute, unmute mic
        /// </summary>
        /// <param name="mute"></param>
        /// <returns></returns>
        public bool MuteMic(bool mute)
        {
            if (skypeLiveSession == null) return false;
            try
            {
                Safe.Lock(locker, 500, () =>
                {
                    if (mute)
                        skypeLiveSession.MuteMyMicrophone();
                    else
                        skypeLiveSession.UnmuteMyMicrophone();
                });
                return true;
            }
            catch
            {
                return false;
            }
        }


        public SktSkype GetSkypeInstance()
        {
            return skype;
        }

        public SktAccount GetAccountInstance()
        {
            return account;
        }
    }
}
